
#include <ctime>
#include <BMap>
#include <BPainter>

#include "LineStripe.h"

LineStripe::LineStripe()
{
	_thickness = 1;
	_length = 0;
	_lineColor.set(128, 128, 128);
}
LineStripe::LineStripe(const BPointArray& linePoints)
{
	_thickness = 1;
	_length = 0;
	_linePoints = linePoints;
	_lineColor.set(128, 128, 128);
}
LineStripe::~LineStripe()
{

}

void LineStripe::copy(const BWidget* other)
{
	BWidget::copy(other);
	if (const LineStripe* stripe = dynamic_cast<const LineStripe*>(other))
	{
		_thickness = stripe->_thickness;
		_linePoints = stripe->_linePoints;
		_length = stripe->_length;
	}
}
BWidget* LineStripe::clone() const
{
	LineStripe* stripe = new LineStripe();
	stripe->copy(this);
	return stripe;
}

BLine2 LineStripe::line(BReal value) const
{
	if (_linePoints.size() < 2)
		return BLine2();
	if (value < 0)
	{
		const BPoint& v0 = _linePoints[0];
		const BPoint& v1 = _linePoints[1];
		return BLine2(v0, v1);
	}
	for (int i = 1; i < _linePoints.size(); i++)
	{
		const BPoint& v0 = _linePoints[i - 1];
		const BPoint& v1 = _linePoints[i];
		BReal len = (v0 - v1).length();
		if (value > len)
		{
			value -= len;
			continue;
		}
		return BLine2(v0, v1);
	}
	const BPoint& v0 = _linePoints.at(_linePoints.size() - 2);
	const BPoint& v1 = _linePoints.at(_linePoints.size() - 1);
	return BLine2(v0, v1);
}
BPoint LineStripe::vertex(BReal value) const
{
	if (_linePoints.size() < 2)
	{
		BPoint point = _linePoints.first();
		return point;
	}
	if (value < 0)
	{
		const BPoint& v0 = _linePoints[0];
		const BPoint& v1 = _linePoints[1];
		BPoint point = v0 + (v1 - v0).normal() * value;
		return point;
	}
	for (int i = 1; i < _linePoints.size(); i++)
	{
		const BPoint& v0 = _linePoints[i - 1];
		const BPoint& v1 = _linePoints[i];
		BReal len = (v0 - v1).length();
		if (value > len)
		{
			value -= len;
			continue;
		}
		BPoint point = v0 + (v1 - v0).normal() * value;
		return point;
	}
	const BPoint& v0 = _linePoints.at(_linePoints.size() - 2);
	const BPoint& v1 = _linePoints.at(_linePoints.size() - 1);
	BPoint point = v1 + (v1 - v0).normal() * value;
	return point;
}
BReal LineStripe::value(const BPoint& point) const
{
	BReal minDist = REAL_MAX;
	BReal value = 0;
	BReal section_begin = 0;
	for (int i = 1; i < _linePoints.size(); i++)
	{
		BPoint v0 = _linePoints[i - 1];
		BPoint v1 = _linePoints[i];
		BLine2 line(v0, v1);
		BPoint vertex = line.closest(point, false);
		BReal dist2 = (vertex - point).length2();
		BReal length = (vertex - v0).length();
		if (dist2 < minDist)
		{
			minDist = dist2;
			value = section_begin + length;
		}
		section_begin += line.length();
	}
	return value;
}

BReal LineStripe::length() const
{
	this->flush();
	return _length;
}

bool LineStripe::closeLoop() const
{
	if (_linePoints.size() < 2)
		return false;
	BReal len2 = (_linePoints.first() - _linePoints.last()).length2();
	if (len2 > REAL_EPSILON)
		return false;
	return true;
}
bool LineStripe::reverse()
{
	if (_linePoints.size())
	{
		_linePoints.reverse();
		this->fresh();
		return true;
	}
	return false;
}

void LineStripe::setLinePoints(const BPointArray& linePoints)
{
	_linePoints = linePoints;
	this->fresh();
}
const BPointArray& LineStripe::linePoints() const
{
	return _linePoints;
}

int LineStripe::pointCount() const
{
	return _linePoints.size();
}
void LineStripe::setPoint(int index, const BPoint& point)
{
	if (_linePoints.set(index, point))
		this->fresh();
}
const BPoint& LineStripe::point(int index) const
{
	return _linePoints.at(index);
}

void LineStripe::setThickness(float thickness)
{
	if (_thickness != thickness)
	{
		_thickness = thickness;
		this->fresh();
	}
}
float LineStripe::thickness() const
{
	return _thickness;
}

void LineStripe::appendPoint(const BPoint& point)
{
	_linePoints.append(point);
	this->fresh();
}
const BPoint& LineStripe::head() const
{
	return _linePoints.first();
}
const BPoint& LineStripe::tail() const
{
	return _linePoints.last();
}

void LineStripe::setLineColor(const BColor& lineColor)
{
	if (_lineColor != lineColor)
	{
		_lineColor = lineColor;
		this->fresh();
	}
}
const BColor& LineStripe::lineColor() const
{
	return _lineColor;
}

void LineStripe::setHeadColor(const BColor& headColor)
{
	if (_headColor != headColor)
	{
		_headColor = headColor;
		this->fresh();
	}
}
const BColor& LineStripe::headColor() const
{
	return _headColor;
}

void LineStripe::setTailColor(const BColor& tailColor)
{
	if (_tailColor != tailColor)
	{
		_tailColor = tailColor;
		this->fresh();
	}
}
const BColor& LineStripe::tailColor() const
{
	return _tailColor;
}

bool LineStripe::query(const BString& text, BValue& value) const
{
	return BWidget::query(text, value);
}

void LineStripe::styleEvent(const BEvent& event)
{

}
void LineStripe::freshEvent(const BEvent& event)
{
	if (_dirty)
	{
		_length = 0;
		for (int i = 1; i < _linePoints.size(); i++)
		{
			const BPoint& v0 = _linePoints[i - 1];
			const BPoint& v1 = _linePoints.at(i);
			_length += (v0 - v1).length();
		}
		_dirty = true;
	}
}
void LineStripe::paintEvent(const BEvent& event)
{
	BWidget::paintEvent(event);
	BPainter painter(this);
	painter.setLineSmooth(true);
	painter.setLineWidth(_thickness);
	painter.setColor(_lineColor);
	painter.drawLineStrip(_linePoints);
}
